#pragma once

#include <iostream>
#include <string>
#include <sys/select.h>
#include <memory>
#include "Log.hpp"
#include "Socket.hpp"

using namespace Net_Work;

const static int gdefaultport = 8888;
const static int gbacklog = 8;
const static int num = sizeof(fd_set) * 8;

class SelectServer
{
private:
    void HandlerEvent(fd_set &rfds)
    {
        for (int i = 0; i < num; i++)
        {
            if (_rfds_array[i] == nullptr)
                continue;
            // 合法的sockfd
            // 读事件分两类，一类是新连接到来。 一类是新数据到来
            int fd = _rfds_array[i]->GetSockFd();
            if (FD_ISSET(fd, &rfds))
            {
                // 读事件就绪
                if (fd == _listensock->GetSockFd())
                {
                    lg.LogMessage(Info, "get a new link\n");
                    // 获取连接
                    std::string clientip;
                    uint16_t clientport;
                    // 不会阻塞！！，因为select已经检测到了listensock已经就绪了
                    Socket *sock = _listensock->AcceptConnection(&clientip, &clientport);
                    if (!sock)
                    {
                        lg.LogMessage(Error, "accept error\n");
                        continue;
                    }
                    lg.LogMessage(Info, "get a client, client info is# %s:%d, fd: %d\n", clientip.c_str(), clientport, sock->GetSockFd());

                    // 这里已经获取连接成功了，接下来怎么办？？？
                    // read？write？绝对不能！！！read 底层数据是否就绪时不确定的！谁清楚fd上面是否有读事件呢？select！
                    // 新链接fd到来的时候，要把新的fd, 想办法交给select托管 -- 只需要添加到数组_rfds_array中即可
                    int pos = 0;
                    for (; pos < num; pos++)
                    {
                        if (_rfds_array[pos] == nullptr)
                        {
                            _rfds_array[pos] = sock;
                            lg.LogMessage(Info, "get a new link, fd is : %d\n", sock->GetSockFd());
                            break;
                        }
                    }
                    if (pos == num)
                    {
                        sock->CloseSocket();
                        delete sock;
                        lg.LogMessage(Warning, "server is full...!\n");
                    }
                }
                else
                {
                    // 普通的读事件就绪
                    // 读数据是有问题的
                    // 这一次读取不会被卡住吗？
                    std::string buffer;
                    bool res = _rfds_array[i]->Recv(&buffer, 1024);
                    if (res)
                    {
                        lg.LogMessage(Info, "client say# %s\n", buffer.c_str());
                        buffer += ": 你好呀，少年";
                        _rfds_array[i]->Send(buffer);
                        buffer.clear();
                    }
                    else
                    {
                        lg.LogMessage(Warning, "client quit, maybe close or error, close fd : %d\n", _rfds_array[i]->GetSockFd());
                        _rfds_array[i]->CloseSocket();
                        delete _rfds_array[i];
                        _rfds_array[i] = nullptr;
                    }
                }
            }
        }
    }

public:
    SelectServer(int port = gdefaultport) : _port(port), _listensock(new TcpSocket()), _isrunning(false)
    {
    }
    void InitServer()
    {
        _listensock->BuildListenSocketMethod(_port, gbacklog);
        for (int i = 0; i < num; i++)
        {
            _rfds_array[i] = nullptr;
        }
        _rfds_array[0] = _listensock.get();
    }
    void Loop()
    {
        _isrunning = true;
        while (_isrunning)
        {
            // 我们能不能直接accept新连接呢?不能！所有的fd，都要交给select. listensock上面新连接，相当于读事件，有新连接，就等价于有新数据到来
            // 首先不能直接accept，而是将listensock交给select。因为只有select有资格知道有没有IO事件就绪
            // 故意放在循环内部
            // 遍历数组，1. 找最大的fd 2. 合法的fd添加到rfds集合中
            fd_set rfds;
            FD_ZERO(&rfds);
            int max_fd = _listensock->GetSockFd();
            for (int i = 0; i < num; i++)
            {
                if (_rfds_array[i] == nullptr)
                {
                    continue;
                }
                else
                {
                    int fd = _rfds_array[i]->GetSockFd();
                    FD_SET(fd, &rfds); // 添加所有合法fd到rfds集合中
                    if (max_fd < fd)   // 更新最大fd
                    {
                        max_fd = fd;
                    }
                }
            }
            // 定义时间
            struct timeval timeout = {0, 0};
            // rfds本质是一个输入输出型参数，rfds是在select调用返回的时候，不断被修改，所以，每次都要重置
            PrintDebug();
            int n = select(max_fd + 1, &rfds, nullptr, nullptr, /*&timeout*/ nullptr);
            switch (n)
            {
            case 0:
                lg.LogMessage(Info, "select timeout..., last time: %u.%u\n", timeout.tv_sec, timeout.tv_usec);
                break;
            case -1:
                lg.LogMessage(Error, "select error!!!\n");
                break;
            default:
                // 正常的就绪的fd
                lg.LogMessage(Info, "select success, begin event handler, last time: %u.%u\n", timeout.tv_sec, timeout.tv_usec);
                HandlerEvent(rfds); // _rfds_array: 3,4,5,6,7,8,9,10 -> rfds: 4,5,6
                break;
            }
        }
        _isrunning = false;
    }
    void Stop()
    {
        _isrunning = false;
    }

    void PrintDebug()
    {
        std::cout << "current select rfds list is : ";
        for (int i = 0; i < num; i++)
        {
            if (_rfds_array[i] == nullptr)
                continue;
            else
                std::cout << _rfds_array[i]->GetSockFd() << " ";
        }
        std::cout << std::endl;
    }
    ~SelectServer()
    {
    }

private:
    std::unique_ptr<Socket> _listensock;
    int _port;
    int _isrunning;

    // select 服务器要被正确设计，需要程序员定义数据结构，来把所有的fd管理起来，往往是数组！
    Socket *_rfds_array[num];
};
